package com.ljy.my_study.java.annotationprocess;

import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.util.ElementScanner8;
import javax.tools.Diagnostic;
import java.util.EnumSet;

public class NameChecker {

    private final Messager messager;

    NameCheckScanner nameCheckScanner=new NameCheckScanner();

    public NameChecker(ProcessingEnvironment processingEnv) {
        this.messager = processingEnv.getMessager();
    }

    public void checkNames(Element element){
        nameCheckScanner.scan(element);
    }

    private class NameCheckScanner extends ElementScanner8<Void,Void>{

        @Override
        public Void visitVariable(VariableElement e, Void unused) {
            if(e.getKind()== ElementKind.ENUM_CONSTANT||e.getConstantValue()!=null||heuristicallyConstant(e)){
                checkAllCaps(e);
            }else{
                checkCamelCase(e,false);
            }
            return super.visitVariable(e, unused);
        }

        private void checkAllCaps(Element e) {
            String name=e.getSimpleName().toString();
            boolean conventional=true;
            int firstCodePoint=name.codePointAt(0);
            if(!Character.isUpperCase(firstCodePoint)){
                conventional=false;
            }else{
                boolean previousUnderscore=false;
                int cp=firstCodePoint;
                for(int i=Character.charCount(cp);i<name.length();i+=Character.charCount(cp)){
                    cp=name.codePointAt(i);
                    if(cp==(int)'_'){
                        if(previousUnderscore){
                            conventional=false;
                            break;
                        }
                        previousUnderscore=true;
                    }else{
                        previousUnderscore=false;
                        if(!Character.isUpperCase(cp)&&!Character.isDigit(cp)){
                            conventional=false;
                            break;
                        }
                    }
                }
            }
            if(!conventional){
                messager.printMessage(Diagnostic.Kind.WARNING,"常量"+name+"应当全部以大写字母或下划线命名，并且以字母开头",e);
            }
        }

        private boolean heuristicallyConstant(VariableElement e) {
            if(e.getEnclosingElement().getKind()==ElementKind.INTERFACE){
                return true;
            }else if(e.getKind()==ElementKind.FIELD&&e.getModifiers().containsAll(EnumSet.of(Modifier.PUBLIC,Modifier.STATIC,Modifier.FINAL))){
                return true;
            }
            return false;
        }

        @Override
        public Void scan(Element e, Void unused) {
            return super.scan(e, unused);
        }

        @Override
        public Void visitType(TypeElement e, Void unused) {
            scan(e.getTypeParameters(),unused);
            checkCamelCase(e,true);
            super.visitType(e,unused);
            return null;
        }

        @Override
        public Void visitExecutable(ExecutableElement e, Void unused) {
            if(e.getKind()== ElementKind.METHOD){
                Name name=e.getSimpleName();
                if(name.contentEquals(e.getEnclosingElement().getSimpleName())){
                    messager.printMessage(Diagnostic.Kind.WARNING,"一个普通方法"+name+"不应当与类名重复，避免与构造函数产生混淆",e);
                    checkCamelCase(e,false);
                }
            }
            super.visitExecutable(e, unused);
            return null;
        }

        private void checkCamelCase(Element e, boolean initalCaps) {
            String name=e.getSimpleName().toString();
            boolean previousUpper=false;
            boolean conventional=true;
            int firstCodePoint=name.codePointAt(0);
            if(Character.isUpperCase(firstCodePoint)){
                previousUpper=true;
                if(!initalCaps){
                    messager.printMessage(Diagnostic.Kind.WARNING,"名称"+name+"应当以小写字母开头",e);
                    return;
                }
            }else if(Character.isLowerCase(firstCodePoint)){
                if(initalCaps){
                    messager.printMessage(Diagnostic.Kind.WARNING,"名称"+name+"应当以大写字母开头",e);
                    return;
                }
            }else{
                conventional=false;
            }
            if(conventional){
                int cp=firstCodePoint;
                for(int i=Character.charCount(cp);i<name.length();i+=Character.charCount(cp)){
                    cp=name.codePointAt(i);
                    if(Character.isUpperCase(cp)){
                        if(previousUpper){
                            conventional=false;
                            break;
                        }
                        previousUpper=true;
                    }else{
                        previousUpper=false;
                    }
                }
            }
            if(!conventional){
                messager.printMessage(Diagnostic.Kind.WARNING,"名称"+name+"应当符合驼峰命名法（Camel Case Names）",e);
            }
        }
    }
}
